home *** CD-ROM | disk | FTP | other *** search
/ Almathera Ten Pack 3: CDPD 3 / Almathera Ten on Ten - Disc 3: CDPD3.iso / scope / 051-075 / scopedisk57 / ipc / ipc.c < prev    next >
C/C++ Source or Header  |  1995-03-19  |  19KB  |  477 lines

  1. /*******************************************************************
  2.  *                                                                 *
  3.  *                           IPC.c                                 *
  4.  *                                                                 *
  5.  *           Inter-Process-Communication Procedures                *
  6.  *                                                                 *
  7.  *              Release  1.0 -- 1988 May 11                        *
  8.  *                                                                 *
  9.  *              Copyright 1988 Peter Goodeve                       *
  10.  *                                                                 *
  11.  *  This source is freely distributable, and may be used freely    *
  12.  *  in any program,  but its functionality should not be modified  *
  13.  *  without prior consultation with the author.  (This is just to  *
  14.  *  prevent proliferation of incompatible variants.  Don't be      *
  15.  *  inhibited from suggesting enhancements!)                       *
  16.  *                                                                 *
  17.  *******************************************************************/
  18.  
  19. /********************************************************************
  20.  *                                                                  *
  21.  *  Synopsis of usage:                                              *
  22.  *  ========                                                        *
  23.  *                                                                  *
  24.  *    Client:                                                       *
  25.  *                                                                  *
  26.  *          port = GetIPCPort(name);                                *
  27.  *      or  port = FindIPCPort(name)                                *
  28.  *          .....                                                   *
  29.  *          msg = CreateIPCMsg(nitems);                             *
  30.  *          .....                                                   *
  31.  *          PutIPCMsg(msg,port);                                    *
  32.  *          .....                                                   *
  33.  *          DeleteIPCMsg(msg);                                      *
  34.  *          .....                                                   *
  35.  *          DropIPCPort(port);                                      *
  36.  *                                                                  *
  37.  *    Server: [standard Exec procedures in brackets]                *
  38.  *                                                                  *
  39.  *          port = ServeIPCPort(name);                              *
  40.  *          .....                                                   *
  41.  *          [WaitPort(port); or Wait(sigbits);]                     *
  42.  *          .....                                                   *
  43.  *          [msg = GetMsg(port);]                                   *
  44.  *          .....                                                   *
  45.  *          [ReplyMsg(msg);]                                        *
  46.  *          .....                                                   *
  47.  *          ShutIPCPort(port);                                      *
  48.  *          <handle remaining messages on the port>                 *
  49.  *          LeaveIPCPort(port);                                     *
  50.  *                                                                  *
  51.  *    Misc.:                                                        *
  52.  *                                                                  *
  53.  *          UseIPCPort(port);                                       *
  54.  *          CheckIPCPort(port,flags);                               *
  55.  *                                                                  *
  56.  *                                                                  *
  57.  ********************************************************************/
  58.  
  59. /********************************************************************
  60.  *                                                                  *
  61.  * These procedures provide a mechanism for independent processes   *
  62.  * to communicate with each other through "IPC ports".   These are  *
  63.  * similar to standard Exec Message Ports, except that they are     *
  64.  * not "owned" by any of the processes; any one process can         *
  65.  * declare itself a "server" (provided no other is currently        *
  66.  * claiming this right), and thus becomes temporarily the handler   *
  67.  * of messages passed to this port.  If there is no server, any     *
  68.  * attempt to send a message to a port will return failure, and     *
  69.  * the client may take whatever action is appropriate.  A client    *
  70.  * may safely "Get" a pointer to a named port (even if there is no  *
  71.  * server yet) and can rely on it remaining valid until it "Drops"  *
  72.  * it again. (In contrast to Exec ports, which have no such         *
  73.  * protection.)                                                     *
  74.  *                                                                  *
  75.  * IPC Ports don't appear in the Exec named port list -- they have  *
  76.  * their own.  There is a single publicly available name, itself    *
  77.  * actually a port on the public list (for reasons we'll see later) *
  78.  * to which is added some data structure including the IPC Port     *
  79.  * list.                                                            *
  80.  *                                                                  *
  81.  * These procedures are just one part of a fully developed system.  *
  82.  * (Another is the IPCMessage structure itself -- see IPC.h and     *
  83.  * elsewhere -- which is essentially independent of the IPC Port    *
  84.  * mechanism.)  They form a fairly self contained set, so they've   *
  85.  * initially been written as a single source file, but there's no   *
  86.  * reason they couldn't be split into smaller segments.  Their      *
  87.  * natural home is a resident library in any case, but that can     *
  88.  * wait for the moment.                                             *
  89.  *                                                                  *
  90.  * Other modules will doubtless have to be added.  One such is      *
  91.  * "LoadIPCPort(name)" which, after doing a GetIPCPort(), will      *
  92.  * check the returned port to see if it has a  server; if not, it   *
  93.  * will check to see if there is a "broker"  process serving the    *
  94.  * IPCBasePort, and if so will send a message to that asking for    *
  95.  * the port to be "served".  (The broker is essentially a server    *
  96.  * like any other -- it will probably look up the port in a user    *
  97.  * supplied list and load the specified server; more advanced       *
  98.  * models may check to see if the server is on another machine,     *
  99.  * for example.)                                                    *
  100.  *                   - - - - - - - - - - -                          *
  101.  *                                                                  *
  102.  *   This code has only been tested under Lattice 4.0.  It is       *
  103.  *   intended to be compiler independent, but please check it       *
  104.  *   for problems.  The code is not always optimized!               *
  105.  *                   - - - - - - - - - - -                          *
  106.  *                                                                  *
  107.  *      %%  The fundamental concept of keeping a use-count  %%      *
  108.  *      %%  on a port is due to Matt Dillon.  Thanks Matt!  %%      *
  109.  *                                                                  *
  110.  ********************************************************************/
  111.  
  112.  
  113. #include "IPCPorts.h"
  114.     /* this should be included first to suppress defaults in IPC.h */
  115.     /* other modules in processes using IPC will not usually need
  116.        this header -- only IPC.h */
  117.  
  118. #include "IPC.h"
  119.     /* note -- the above includes prototypes for the procedures below */
  120.  
  121. #include "exec/memory.h"
  122. #include "exec/tasks.h"
  123.  
  124. struct Task * FindTask();
  125.  
  126.  
  127. /*
  128.  *  IPCBasePort is the node around which the whole IPC system is organized.
  129.  *  It is placed in AmigaExec's public port list by the first program to
  130.  *  use IPC, and remains there ever after.  It is the ONLY public port
  131.  *  created by the IPC system -- all IPCPorts are hung on a list from a
  132.  *  header in the IPCBasePort structure.  It may also be an actual port
  133.  *  served by a "broker" process, which will be sent messages requesting,
  134.  *  for example, that it load a server for a port.
  135.  */
  136.  
  137. struct IPCBasePort *IPCBasePort = NULL;
  138.  
  139.  
  140. /*
  141.  *  OpenIPCBase
  142.  *
  143.  *      returns a pointer to IPCBasePort, creating, initializing and
  144.  *      adding it to the Exec public port list if it doesn't already exist.
  145.  *      This procedure will normally only be called by other IPC system
  146.  *      procedures (except possibly in a program such as a "broker").
  147.  */
  148.  
  149. struct IPCBasePort * OpenIPCBase()
  150. {
  151.     char * pname = "IPC_Base_Port";
  152.     struct MsgPort * port;
  153.  
  154.     if (IPCBasePort) return IPCBasePort;
  155.     Forbid();
  156.     if (!(IPCBasePort = (struct IPCBasePort *)FindPort(pname))) {
  157.         IPCBasePort = (struct IPCBasePort *)
  158.             AllocMem(sizeof(struct IPCBasePort), MEMF_CLEAR | MEMF_PUBLIC);
  159.         if (IPCBasePort) {
  160.             port = (struct MsgPort *) IPCBasePort;
  161.             port->mp_Node.ln_Name = IPCBasePort->ipcb_Port.ipp_Name;
  162.             port->mp_Node.ln_Type = NT_MSGPORT;
  163.             AddPort(port);
  164.             NewList(&(IPCBasePort->ipcb_PortList));
  165.             strcpy(IPCBasePort->ipcb_Port.ipp_Name, pname);
  166.         }
  167.     }
  168.     Permit();
  169.     return IPCBasePort;
  170. }
  171.  
  172. /*** There is NO CloseIPCBase()!! (it stays around forever) ***/
  173.  
  174.  
  175. /*
  176.  *  CreateIPCPort is a private procedure which should not be called
  177.  *  directly by a user program.  (Actually it is only called by GetIPCPort
  178.  *  but for clarity it is kept separate.)
  179.  *  The created IPCPort will be added to the list in IPCBasePort
  180.  *  (GetIPCPort won't allow duplicate names) unless the name is NULL,
  181.  *  in which case an "anonymous" port will be created that does not get
  182.  *  onto the list; such anonymous ports can only be accessed by other
  183.  *  processes if they are themselves passed as pointers in messages.
  184.  */
  185.  
  186. static struct IPCPort * CreateIPCPort(name) char *name;
  187. {
  188.     struct IPCPort * port;
  189.     int psize;
  190.  
  191.     if (!OpenIPCBase()) return NULL; /* Quick check before we do anything */
  192.     psize = sizeof(struct IPCPort) + (name ? strlen(name) : 0);
  193.         /* psize is actually one byte too big -- do you care? */
  194.     port = (struct IPCPort *)
  195.            AllocMem(psize, MEMF_CLEAR | MEMF_PUBLIC);
  196.  
  197.     if (port) {
  198.         port->ipp_Size = psize;
  199.         NewList(&(port->ipp_Port.mp_MsgList));
  200.         port->ipp_Port.mp_Node.ln_Type = NT_MSGPORT;
  201.         port->ipp_Port.mp_Flags = PA_IGNORE; /* initially */
  202.         if (name) { /* anonymous port is not put on list */
  203.           port->ipp_Port.mp_Node.ln_Name = port->ipp_Name;
  204.                     /* point to name storage array */
  205.           strcpy(port->ipp_Name, name); /* move name to permanent storage */
  206.           AddHead(&IPCBasePort->ipcb_PortList, port);
  207.         }
  208.     }
  209.     return port;
  210. }
  211.  
  212.  
  213. /*
  214.  *  FindIPCPort
  215.  *
  216.  *     Finds the IPCPort with the name supplied as argument if
  217.  *     it has been previously created.
  218.  *     Returns pointer to port if it exists -- null otherwise;
  219.  *     registers a new connection to the port (i.e. increments UseCount).
  220.  *     (Connection must be terminated when program is done by the procedure
  221.  *     DropIPCPort.)
  222.  *     If the current server has set the IPP_NOTIFY flag in the port, the
  223.  *     server task will be signalled using the port signal bit.  NOTE that
  224.  *     WaitPort will NOT detect these signals, because no message is
  225.  *     actually sent; the program must do a Wait on this bit, and should do
  226.  *     a GetMsg as usual, but if the message pointer is null it should
  227.  *     then call, for example, CheckIPCPort.
  228.  */
  229.  
  230. struct IPCPort * FindIPCPort(name) char *name;
  231. {
  232.     struct IPCPort * port;
  233.     if (!OpenIPCBase()) return NULL; /* Quick check before we do anything */
  234.     if (!name) return NULL; /* port could be anonymous */
  235.     Forbid();
  236.     port = (struct IPCPort *)FindName(&IPCBasePort->ipcb_PortList, name);
  237.     if (port) port->ipp_UseCount++;
  238.     Permit();
  239.     if (port && port->ipp_Flags & IPP_NOTIFY)
  240.         /* Server wants to be notified */
  241.         Signal(port->ipp_Port.mp_SigTask,
  242.                1<<port->ipp_Port.mp_SigBit);
  243.     return port;
  244. }
  245.  
  246.  
  247. /*
  248.  *  GetIPCPort
  249.  *
  250.  *     Returns a pointer to IPCPort with the name supplied as an argument;
  251.  *     unlike FindIPCPort, it always returns pointer to port -- this is
  252.  *     created if it doesn't exist; registers a new connection to the port
  253.  *     (use DropIPCPort when done).  It will notify a server if requested
  254.  *     (see FindIPCPort).
  255.  */
  256.  
  257. struct IPCPort * GetIPCPort(name) char *name;
  258. {
  259.     struct IPCPort * port;
  260.     Forbid();
  261.     port = FindIPCPort(name);
  262.     if (!port) {
  263.         port = CreateIPCPort(name);
  264.         port->ipp_UseCount++;
  265.     }
  266.     Permit();
  267.     return port;
  268. }
  269.  
  270.  
  271. /*
  272.  *  UseIPCPort
  273.  *
  274.  *     Registers another connection to a port (normally when the port
  275.  *     was passed as a pointer from another process).
  276.  *     (Use DropIPCPort when done.)
  277.  */
  278.  
  279. void UseIPCPort(port) struct IPCPort * port;
  280. {
  281.     port->ipp_UseCount++;
  282.     if (port->ipp_Flags & IPP_NOTIFY) /* Server wants to be notified */
  283.         Signal(port->ipp_Port.mp_SigTask,
  284.                1<<port->ipp_Port.mp_SigBit);
  285. }
  286.  
  287.  
  288. /*
  289.  *  DropIPCPort
  290.  *
  291.  *     Terminate a connection to a port established by FindIPCPort,
  292.  *     GetIPCPort, or UseIPCPort.  Port will be destroyed if there are
  293.  *     no other connections left.
  294.  *     If the IPP_NOTIFY flag is set in the port, the server will be
  295.  *     signalled when this procedure is called (see FindIPCPort).
  296.  */
  297.  
  298. void DropIPCPort(port) struct IPCPort * port;
  299. {
  300.     if (!port) return; /* to save the client some trouble
  301.                           (in a cleanup procedure) */
  302.     Forbid();
  303.     if (--port->ipp_UseCount == 0) {
  304.         /* an anonymous port is NOT on list -- ALL others MUST be! */
  305.         if (port->ipp_Port.mp_Node.ln_Name) Remove(port);
  306.         Permit();
  307.         FreeMem(port, port->ipp_Size);
  308.     }
  309.     else {
  310.         if (port->ipp_Flags & IPP_NOTIFY) /* Server wants to be notified */
  311.             Signal(port->ipp_Port.mp_SigTask,
  312.                    1<<port->ipp_Port.mp_SigBit);
  313.         Permit();
  314.     }
  315. }
  316.  
  317.  
  318. /*
  319.  *  ServeIPCPort
  320.  *
  321.  *     Registers calling task as the server on the named port (which is
  322.  *     created if it doesn't exist); null is returned if the port already
  323.  *     has a server, otherwise a pointer to it is returned.  At the same
  324.  *     time the port is given the server as its SigTask and a suitable
  325.  *     signal bit is allocated.
  326.  */
  327.  
  328. struct IPCPort * ServeIPCPort(name) char *name;
  329. {
  330.     struct IPCPort * port;
  331.  
  332.     port = GetIPCPort(name);
  333.     if (port) {
  334.         Forbid();
  335.         if ((port->ipp_Flags & (IPP_SERVED | IPP_SHUT))
  336.          || (port->ipp_Port.mp_SigBit = AllocSignal(-1)) == -1) {
  337.             DropIPCPort(port);
  338.             port = NULL;
  339.         }
  340.         else {
  341.             port->ipp_Port.mp_Flags = PA_SIGNAL;
  342.             port->ipp_Port.mp_SigTask = FindTask(0);
  343.             port->ipp_Flags |= IPP_SERVED;
  344.         }
  345.         Permit();
  346.     }
  347.     return port;
  348. }
  349.  
  350. /*
  351.  *  ShutIPCPort
  352.  *
  353.  *     ONLY the current server may call this procedure.
  354.  *     It prevents more messages being sent to this port, but
  355.  *     does not end server connection; remaining messages can be dealt
  356.  *     with before finally calling LeaveIPCPort.
  357.  *     Note that it does NOT inhibit the server being signalled if
  358.  *     IPP_NOTIFY is set and another client connects.  (At the moment
  359.  *     there is no mechanism to reopen a shut port without Leaving
  360.  *     first; this may be possible in a future revision.)
  361.  */
  362.  
  363. void ShutIPCPort(port) struct IPCPort * port;
  364. {
  365.     port->ipp_Flags |= IPP_SHUT; /* Prevent other servers connecting */
  366.     port->ipp_Flags &= ~IPP_SERVED; /* prevent messages from landing */
  367.     port->ipp_Port.mp_Flags = PA_IGNORE; /* someone might use PutMsg! */
  368. }
  369.  
  370. /*
  371.  *  LeaveIPCPort
  372.  *
  373.  *     ONLY the current server may call this procedure.
  374.  *     Disconnects the server process from the port; another process
  375.  *     can then become a server if it desires.  If there are no other
  376.  *     connections, the port is removed.
  377.  */
  378.  
  379. void LeaveIPCPort(port) struct IPCPort * port;
  380. {
  381.     FreeSignal(port->ipp_Port.mp_SigBit);
  382.     port->ipp_Port.mp_SigTask = NULL;
  383.     port->ipp_Flags &= ~(IPP_SHUT | IPP_SERVED | IPP_SERVER_FLAGS);
  384.     DropIPCPort(port);
  385. }
  386.  
  387.  
  388. /*
  389.  *  CheckIPCPort
  390.  *
  391.  *     Returns the number of current connections to this port (including
  392.  *     the server); a call by the server (only) will also set the (user
  393.  *     settable) port flags to the value in the second argument --
  394.  *     currently the only valid flag is IPP_NOTIFY.
  395.  */
  396.  
  397. CheckIPCPort(port, flags)
  398.     struct IPCPort *port;
  399.     USHORT flags;
  400. {
  401.     if (port->ipp_Port.mp_SigTask == FindTask(0))
  402.         /* only server can change flags */
  403.         port->ipp_Flags = (port->ipp_Flags & ~IPP_SERVER_FLAGS) |
  404.                           (flags & IPP_SERVER_FLAGS);
  405.     return (int) port->ipp_UseCount;
  406. }
  407.  
  408.  
  409. /*
  410.  *  PutIPCMsg
  411.  *
  412.  *     Sends an IPCMessage to an IPCPort; if the port has no server or
  413.  *     is shut, the message is not sent and the function returns FALSE;
  414.  *     otherwise it returns TRUE. (Other port flags to be added later
  415.  *     may affect these actions.)
  416.  *     Note that a ReplyPort should be supplied in the message as usual
  417.  *     (except for the rare possible usage where a message is not to
  418.  *     be replied; in this case, the IPC_TRANSFER flag must be set in
  419.  *     both the message and all the items it contains, and the ReplyPort
  420.  *     must be NULL).
  421.  */
  422.  
  423. PutIPCMsg(port,msg)
  424.     struct IPCPort *port;
  425.     struct IPCMessage *msg;
  426. {
  427.     Forbid(); /* we do this the straightforward way -- it's very quick */
  428.     if (port->ipp_Flags & IPP_SERVED) {
  429.         PutMsg(port, msg);
  430.         Permit();
  431.         return TRUE;
  432.     }
  433.     Permit();
  434.     return FALSE;
  435. }
  436.  
  437.  
  438. /*
  439.  *  CreateIPCMsg
  440.  *
  441.  *     Creates a standard IPCMessage block (in MEMF_PUBLIC) with the
  442.  *     number of IPCItems supplied as argument. (Special cases -- like
  443.  *     in-line data -- you will have to handle yourself, and you always
  444.  *     have to manage the data blocks yourself).
  445.  */
  446.  
  447. struct IPCMessage * CreateIPCMsg(nitems)
  448. {
  449.     int msgsize;
  450.     struct IPCMessage * mp;
  451.     msgsize = sizeof(struct IPCMessage) +
  452.                   (nitems -1)*sizeof(struct IPCItem);
  453.     mp = (struct IPCMessage *)
  454.          AllocMem(msgsize, MEMF_CLEAR | MEMF_PUBLIC);
  455.     if (mp) {
  456.         mp->ipc_Msg.mn_Length = msgsize - sizeof(struct Message);
  457.         mp->ipc_ItemCount = nitems;
  458.     }
  459.     return  mp;
  460. }
  461.  
  462.  
  463. /*
  464.  *  DeleteIPCMsg
  465.  *
  466.  *     Deletes a standard IPCMessage block;  you must first have disposed
  467.  *     of any attached data as appropriate.
  468.  */
  469.  
  470. void DeleteIPCMsg(msg) struct IPCMessage *msg;
  471. {
  472.     FreeMem(msg, sizeof(struct Message) + msg->ipc_Msg.mn_Length);
  473. }
  474.  
  475.  
  476.             /*********************************************/
  477.